import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

public class TCPServer {

    static class User{
        private String username;
        private Socket socket;
        public User(String username, Socket socket) {
            this.username = username;
            this.socket = socket;
        }

        public String getUsername() {
            return username;
        }

        public Socket getSocket() {
            return socket;
        }
    }

    public static void main(String[] args) {

        // 使用CopyOnWriteArrayList来处理并发修改问题，它在迭代时不需要额外的同步
        CopyOnWriteArrayList<User> socketList = new CopyOnWriteArrayList<>();

        try {
            ServerSocket serverSocket = new ServerSocket(8088);
            System.out.println("服务器已启动，正在等待客户端连接...");

            while (true){
                Socket clientSocket = serverSocket.accept();

                // 从客户端输入流读取用户名
                InputStream inputStream = clientSocket.getInputStream();
                byte[] buffer = new byte[1024];
                int length = inputStream.read(buffer);

                if (length == -1) {
                    // 如果没发送用户名，关闭连接并返回
                    clientSocket.close();
                    return;
                }
                String username = new String(buffer, 0, length);
                // 验证用户名
                if(username.isEmpty() || username.equals("已离线")){
                    clientSocket.close();
                    return;
                }

                // 将用户名发送到之前连接到的所有客户端
                for (User user : socketList) {
                    String message = username + "已加入群聊";
                    OutputStream outputStream = user.getSocket().getOutputStream();
                    outputStream.write(message.getBytes());
                }
                User clientUser = new User(username, clientSocket);
                socketList.add(clientUser);
                System.out.println(username + "已连接到服务器, 当前连接数: " + socketList.size());

                // 控制线程的关闭
                AtomicBoolean clientThreadCon = new AtomicBoolean(true);
                // 为每个新连接的客户端启动一个线程来处理消息接收和转发
                Thread clientThread = new Thread(() -> {
                    try {
                        // 不断读入客户端发送的其他数据
                        while (clientThreadCon.get()) {
                            int len = inputStream.read(buffer);
                            if (len == -1) {
                                continue;
                            }
                            String message = new String(buffer, 0, len);
                            System.out.println(username + "发送消息：" + message);

                            // 用户主动离线，发送离线标识给服务端
                            if(message.equals("已离线")){
                                clientThreadCon.set(false);
                                break;
                            }
                            for (User user : socketList) {
                                if(user.getUsername().equals(username)){
                                    user.getSocket().getOutputStream().write(("我: " + message).getBytes());
                                }else {
                                    user.getSocket().getOutputStream().write((username + ": " + message).getBytes());
                                }
                            }
                        }
                    } catch (IOException e) {
                        System.out.println(username + "用户强制断线");
                        e.printStackTrace();
                    } finally {
                        try {
                            // 当客户端断开连接时，从列表中移除该客户端的Socket并关闭连接
                            socketList.remove(clientUser);
                            clientSocket.close();
                            clientThreadCon.set(false);
                            System.out.println(username + "已离线，当前连接数：" + socketList.size());

                            for (User user : socketList) {
                                user.getSocket().getOutputStream().write((username + ": 已离线").getBytes());
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                });
                clientThread.start();
            }

        } catch (IOException e){
            e.printStackTrace();
        }

    }
}